/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package entityappclient;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Mark
 */
public class ConsoleTable {

    public static final int MAX_WIDTH = 79; //-1 For end column line
    public static final char HORIZONTAL_SYMBOL = '-';
    public static final char VERTICAL_SYMBOL = '|';
    public static final char CROSS_SYMBOL = '+';
    public static final char BLANK_SYMBOL = ' ';
    private int columns;
    private String[] columnHeaders;
    private int[] columnWidths;
    private ArrayList<List<String>> rows;
    private int pageSize;


    /**
     * Creates a new table object with the specified number of columns
     *
     * @param columns Maximum of 26 (leaves one space for data)
     */
    public ConsoleTable(int columns) {
        this.columns = columns;
        this.columnHeaders = new String[columns];
        this.columnWidths = new int[columns];
        //Minimum for column width is 3
        for (int x = 0; x < columns; x++) {
            columnWidths[x] = 3;
        }
        this.rows = new ArrayList();
        this.pageSize = 10;
    }
    
    //Pre-setup customer table
    public static ConsoleTable getCustomerTable() {
        ConsoleTable ct = new ConsoleTable(7);
        ct.setHeader(0, "ID", 3);
        ct.setHeader(1, "Name", 10);
        ct.setHeader(2, "Address", 15);
        ct.setHeader(3, "Phone", 10);
        ct.setHeader(4, "Password", 10);
        ct.setHeader(5, "Teller", 6);
        ct.setHeader(6, "Disabled", 8);      
        return ct;
    }
    
    //Pre-setup account table
    public static ConsoleTable getAccountTable() {
        ConsoleTable ct = new ConsoleTable(5);
        ct.setHeader(0, "ID", 3);
        ct.setHeader(1, "Type", 4);
        ct.setHeader(2, "Balance", 10);
        ct.setHeader(3, "Owner ID", 8);
        ct.setHeader(4, "Disabled", 8);
        return ct;
    }
    
    //Pre-setup transaction table
    public static ConsoleTable getTransactionTable() {
        ConsoleTable ct = new ConsoleTable(7);
        ct.setHeader(0, "ID", 3);
        ct.setHeader(1, "Type", 4);
        ct.setHeader(2, "Timestamp", 19);
        ct.setHeader(3, "Amount", 10);
        ct.setHeader(4, "Description", 20);
        ct.setHeader(5, "From", 5);
        ct.setHeader(6, "To", 5);
        return ct;
    }

    public int getColumns() {
        return columns;
    }

    public int getTotalRows() {
        return rows.size();
    }

    public int getPageSize() {
        return pageSize;
    }
    
    public int getNoOfPages() {
        return (int) Math.ceil((double)rows.size()/pageSize);
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }
    
    /**
     * Returns the width of the specified column.
     *
     * @param column Which column to get width of (0 bound index)
     * @return Width of requested column
     */
    public int getColumnWidth(int column) {
        if (column >= this.columns) {
            throw new IndexOutOfBoundsException("Column argument invalid: " + column);
        }
        return this.columnWidths[column];
    }

    /**
     * Returns the header text of the specified column. Returns the header text
     * of the specified column.
     *
     * @param column Which column to get header text of (0 bound index)
     * @return
     */
    public String getColumnHeader(int column) {
        if (column >= this.columns) {
            throw new IndexOutOfBoundsException("Column argument invalid: " + column);
        }
        return this.columnHeaders[column];
    }

    /**
     * Will set a columns width and header text. Will throw IndexOutOfBounds
     * error if invalid column is provided
     *
     * @param column Which column to set (0 bound index)
     * @param headerText Text to be displayed at top of table
     * @param columnWidth How wide the column should be (min 3)
     * @return True if successful, false is columnWidth would cause it to exceed
     * maximum width (MAX_WIDTH)
     */
    public boolean setHeader(int column, String headerText, int columnWidth) {
        //Check if column is in range
        if (column >= this.columns) {
            throw new IndexOutOfBoundsException("Column argument invalid: " + column);
        }
        //Check if columnWidth is valid (>3)
        if (columnWidth < 3) {
            throw new IllegalArgumentException("Column With must be greater than 3");
        }
        //Calculate to column length
        int totalWidth = 0;
        for (int x = 0; x < columns; x++) {
            //Exclude column to be changed
            if (x != column) {
                totalWidth += this.columnWidths[x];
            }
        }
        //Check column length is valid
        if (totalWidth + columnWidth > MAX_WIDTH) {
            //Total width exceeds 80
            return false;
        }
        //Set column width
        this.columnWidths[column] = columnWidth;
        //Determine if string needs to be padded or trimed

        if (columnWidth > headerText.length()) {
            //Needs to be padded and centered
            //Work out padding needed on each side
            int frontPadding = (int) Math.ceil((float) (columnWidth - headerText.length()) / 2);
            int endPadding = (int) Math.floor((float) (columnWidth - headerText.length()) / 2);
            for (int x = 0; x < frontPadding; x++) {
                //Add a space
                headerText = BLANK_SYMBOL + headerText;
            }
            for (int x = 0; x < endPadding; x++) {
                headerText += BLANK_SYMBOL;
            }
        } else {
            //Needs to be trimmed
            headerText = headerText.substring(0, columnWidth);
        }
        //Set header text
        this.columnHeaders[column] = headerText;
        return true;
    }

    /**
     * Accepts an array of strings and adds a new with the data supplied in the
     * array
     *
     * @param newRow
     */
    public void addRow(String[] newRow) {
        //Create list to hold items
        List<String> row = new ArrayList();

        //Loop through all fields
        for (int x = 0; x < columns; x++) {
            row.add(newRow[x]);
        }
        //Add row to group
        rows.add(row);
    }

    private void printLineBreak() {
        //For each column
        for (int x = 0; x < columns; x++) {
            //Print cross
            System.out.print(CROSS_SYMBOL);
            //Print horizontal line for column width
            for (int y = 0; y < columnWidths[x]; y++) {
                System.out.print(HORIZONTAL_SYMBOL);
            }
        }
        System.out.println(CROSS_SYMBOL);
    }

    private void printHeaderRow() {
        for (int x = 0; x < columns; x++) {
            System.out.print(VERTICAL_SYMBOL);
            System.out.print(columnHeaders[x]);
        }
        System.out.println(VERTICAL_SYMBOL);
    }

    private void printHeaders() {
        //Print top table border
        printLineBreak();
        //Print column names
        printHeaderRow();
        printLineBreak();
    }

    private void printRow(int rowNo) {
        //Get the row
        List<String> row = rows.get(rowNo);

        //For each column
        for (int x = 0; x < row.size(); x++) {
            System.out.print(VERTICAL_SYMBOL);
            //Check if padding or trim is needed
            if (columnWidths[x] > row.get(x).length()) {
                //Needs to be padded
                //Print out text
                System.out.print(row.get(x));
                //Pad with spaces
                for (int y=0; y < (columnWidths[x] - row.get(x).length()); y++) {
                    System.out.print(BLANK_SYMBOL);
                }
            } else {
                //Needs to be trimmed
                System.out.print(row.get(x).substring(0, columnWidths[x]));
            }
        }
        //Close off row
        System.out.println(VERTICAL_SYMBOL);
    }
    
    /**
     * Prints a page of the table. Page size is dictated by pageSize
     * @param page Which page to print
     */
    public void printPagedTable(int page) {
        //Print the header
        printHeaders();
        //Calculate start and end pages
        int startPage = page*pageSize;
        int endPage = Math.min(rows.size(), (page+1)*pageSize);
        //Print page of rows
        for (int x=page*pageSize; x < endPage; x++) {
            printRow(x);
        }
        //Print bottom table border
        printLineBreak();
    }
    
    public void printTable() {
        //Print the header
        printHeaders();
        //Print each row
        for (int x=0; x < rows.size(); x++) {
            printRow(x);
        }
        //Print bottom table border
        printLineBreak();
        
    }
}
